#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <cassert>
#include <pthread.h>
#include <string>
#include <ctime>

using namespace std;

// pthread_mutex_t是原生线程库提供的数据类型
// pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

#define THREAD_NUM 5

class ThreadData
{
public:
    ThreadData(const string& tname, pthread_mutex_t* pmtx)
        : _tname(tname)
        , _pmtx(pmtx)
    {}

    string _tname;
    pthread_mutex_t* _pmtx;
};

int tickets = 10000; // 临界资源

void* getTickets(void* args)
{
    ThreadData* td = (ThreadData*)args;
    while(true)
    {
        // 只有获得锁的线程才能执行进入临界区,访问临界资源
        int n = pthread_mutex_lock(td->_pmtx);
        assert(n == 0); 
        (void)n;
        // 加锁和解锁之间的代码就是临界区
        if(tickets > 0)
        {
            usleep(rand() % 2000);
            printf("%s: %d\n", td->_tname.c_str(), tickets--);
            pthread_mutex_unlock(td->_pmtx);
        }
        else   
        {
            pthread_mutex_unlock(td->_pmtx);
            break;
        }

        // 抢到票后的其它逻辑
        usleep(rand() % 2000);
    }

    delete td;  // 票数为0时,释放ThreadData
    return nullptr;
}

int main()
{
    int begin = time(nullptr);
    pthread_mutex_t mtx;
    // 第二个参数为锁的属性,设置为nullptr表示不关心
    pthread_mutex_init(&mtx, nullptr); // 初始化锁 

    pthread_t t[THREAD_NUM];
    for(int i = 0; i < THREAD_NUM; ++i)
    {
        string name = "thread";
        name += to_string(i + 1);
        ThreadData* td = new ThreadData(name, &mtx);
        pthread_create(t + i, nullptr, getTickets, (void*)td);
    }

    for(int i = 0; i < THREAD_NUM; ++i)
    {
        pthread_join(t[i], nullptr);
    }

    
    pthread_mutex_destroy(&mtx); // 销毁锁
    int end = time(nullptr);
    cout << "time: " << end - begin << endl;
}


// int main()
// {
//     // 多线程抢票
//     srand((unsigned int)time(nullptr) ^ getpid() ^ 0x258);
//     pthread_t t1, t2, t3;
//     pthread_create(&t1, nullptr, getTickets, (void*)"thread one");
//     pthread_create(&t2, nullptr, getTickets, (void*)"thread two");
//     pthread_create(&t3, nullptr, getTickets, (void*)"thread three");

//     pthread_join(t1, nullptr);
//     pthread_join(t2, nullptr);
//     pthread_join(t3, nullptr);

//     return 0;
// }
